#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
//要以文件IO的思想去学习这个IPC通信

/*
    实现两个无血缘关系进程的通信
*/
/*
底层原理：
    物理内存页映射到多个进程的虚拟地址空间 (页表)。
    shmget(创建/获取) -> shmat(映射)。
    内核仅初始管理。
适用场景：
    需要极高速度和大容量数据交换的场景 (大型科学计算、数据库、图形处理)；
    进程紧密协作。
优点：
    速度最快 (零内核拷贝)；
    适合大数据量。
缺点：
    无内置同步 (必须配合信号量/互斥锁)；
    内存管理复杂；
    系统范围资源需管理 (SysV)；
    数据易失。
数据读取与时效性：
    可重复读取。
    进程直接访问内存。
    无内置机制保证时效性！ 读取的可能是旧数据、部分更新或冲突写入。
    必须严格使用同步机制 (信号量、锁、内存屏障) 来协调读写，确保读取最新、一致的数据。
*/

typedef struct shm_mem_info{
    pid_t read_pid;
	pid_t write_pid;
    char buf[1024];
}mem_info_t;

void sighandler(int signum)
{
    //这个打印不可加，因为父子进程都可能会调用该函数
    //printf("process pid = [%d], recv [%s]\n", getpid(), signum);
    return ;
}

int main(int argc, char* argv[]){
    int ret = 0;
	int len = 0;
    char buf[1024] = {0};
    //1、这个key用于两个非血缘关系的进程实现通信，如果只是父子进程通信，可以不设置这个key,用IPC_PRIVATE代替
    key_t key = ftok("./tmp_mem.txt", 'a');
    if(key < 0){
        perror("ftok() error:");
        exit(-1);
    }
    printf("key = [%ld]\n", key);

    //2、在内核中创建一块共享内存
    int shmid = shmget(key, 1024, IPC_CREAT|0777);
    if(shmid < 0){
        perror("shmget() error:");
        exit(-1);
    }
    
    //3、即将内核中共享内存区域映射到用户区的进程地址空间中，节省了调用开销
    mem_info_t *mem_info = (mem_info_t *)shmat(shmid, NULL, 0);
    if(mem_info == NULL){
        perror("shmat() error:");
        exit(-1);
    }    
    system("ipcs -m");

	//注册信号处理函数，用于唤醒pause()
	signal(SIGUSR1, sighandler);

	mem_info->write_pid = getpid();
	pause(); //wait read 进程把进程号读走
	
	//pause 被唤醒后，此时的内存中存的是write的进程id
	printf("write process: mem_info->read_pid = [%d]\n", mem_info->read_pid);
	
    //4、写共享内存
    while(1){
		if(!strncmp(mem_info->buf, "exit", 4)){
			break;
		}
        memset(mem_info->buf, 0, sizeof(mem_info->buf));
        printf("write shared memory:");
        fgets(mem_info->buf, sizeof(mem_info->buf), stdin);
	    len = strlen(mem_info->buf);
        if (len > 0 && mem_info->buf[len - 1] == '\n') {
            mem_info->buf[len - 1] = '\0';  // 替换换行符
        }
        kill(mem_info->read_pid, SIGUSR2);
        pause(); //wait 子进程读取共享内存中的数据
    }
    
    //5、将进程里的地址映射删除
    ret = shmdt(mem_info);
    if(ret < 0){
        perror("shmdt() error:");
        exit(-1);
    }
	//1秒时间用于cpu易主，让read进程第一时间删除共享内存
	sleep(1);
    system("ipcs -m");
    return 0;    
}
